﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Extensions;

namespace MetaSharp.Transformation.Grammar.Tests.TokenizerTests
{
    public class IdentifierTests
    {
        Tokenizer tokenizer = new Tokenizer();

        [Theory]
        [InlineData("A")]
        [InlineData("a")]
        [InlineData("ǅ")]
        [InlineData("ʰ")]
        [InlineData("ª")]
        [InlineData("ↂ")]
        [InlineData("\\u0041")] // A
        [InlineData("\\u0061")] // a
        [InlineData("\\u01C5")] // ǅ
        [InlineData("\\u02B0")] // ʰ
        [InlineData("\\u00AA")] // ª
        [InlineData("\\u2182")] // ↂ
        [InlineData("\\U00000041")]
        [InlineData("@x")]
        [InlineData("_abc")]
        [InlineData("@_abc")]
        [InlineData("a1b2c3")]
        public void CanParseIdentifier(string identifier)
        {
            var p = tokenizer.Identifier.ToPatternMatch();
            var s = new Scope(new Stream(identifier));
            var m = p(s);

            Assert.True(m.Matched);
        }

        [InlineData("A")]
        [InlineData("a")]
        [InlineData("ǅ")]
        [InlineData("ʱ")]
        [InlineData("ƻ")]
        [InlineData("Ⅷ")]
        [Theory]
        public void CanParseLetterCharacters(string letter)
        {
            var p = tokenizer.LetterCharacter.ToPatternMatch();
            var s = new Scope(new Stream(letter));
            var m = p(s);
            Assert.True(m.Matched);
        }

        [InlineData('\u0041', "\\u0041")]
        [InlineData('\U0000A666', "\\U0000A666")]
        [Theory]
        public void UnicodeLetterCharactersFormatToStrings(char expected, string unicodeLetter)
        {
            var p = tokenizer.LetterCharacter.ToPatternMatch();
            var s = new Scope(new Stream(unicodeLetter));
            var m = p(s);

            Assert.Equal(expected, m.Value);
        }

        [InlineData("\\u0020")]
        [InlineData("\\U00000020")]
        [Theory]
        public void CantParseNonLetterCharacterValuesFromHex(string nonletter)
        {
            var p = tokenizer.LetterCharacter.ToPatternMatch();
            var s = new Scope(new Stream(nonletter));
            var m = p(s);
            Assert.False(m.Matched);
        }

        [InlineData("̀")]  // COMBINING GRAVE ACCENT (U+0300)
        [InlineData("ः")] // DEVANAGARI SIGN VISARGA 
        [Theory]
        public void CanParseCombiningCharacters(string c)
        {
            var p = tokenizer.CombiningCharacter.ToPatternMatch();
            var s = new Scope(new Stream(c));
            var m = p(s);
            Assert.True(m.Matched);
        }

        [Fact]
        public void CanParseDecimalDigit()
        {
            var p = tokenizer.DecimalDigitCharacter.ToPatternMatch();
            var s = new Scope(new Stream("0"));
            var m = p(s);
            Assert.True(m.Matched);
        }

        [Fact]
        public void CanParseConnectingCharacter()
        {
            var p = tokenizer.ConnectingCharacter.ToPatternMatch();
            var s = new Scope(new Stream("‿"));
            var m = p(s);
            Assert.True(m.Matched);
        }

        [Fact]
        public void CanParseFormattaingCharacter()
        {
            var p = tokenizer.FormattingCharacter.ToPatternMatch();
            var s = new Scope(new Stream("۝"));
            var m = p(s);
            Assert.True(m.Matched);
        }


        [InlineData("abstract")]
        [InlineData("as")]
        [InlineData("base")]
        [InlineData("bool")]
        [InlineData("break")]
        [InlineData("byte")]
        [InlineData("case")]
        [InlineData("catch")]
        [InlineData("char")]
        [InlineData("checked")]
        [InlineData("class")]
        [InlineData("const")]
        [InlineData("continue")]
        [InlineData("decimal")]
        [InlineData("default")]
        [InlineData("delegate")]
        [InlineData("do")]
        [InlineData("double")]
        [InlineData("else")]
        [InlineData("enum")]
        [InlineData("event")]
        [InlineData("explicit")]
        [InlineData("extern")]
        [InlineData("false")]
        [InlineData("finally")]
        [InlineData("fixed")]
        [InlineData("float")]
        [InlineData("for")]
        [InlineData("foreach")]
        [InlineData("goto")]
        [InlineData("if")]
        [InlineData("implicit")]
        [InlineData("in")]
        [InlineData("int")]
        [InlineData("interface")]
        [InlineData("internal")]
        [InlineData("is")]
        [InlineData("lock")]
        [InlineData("long")]
        [InlineData("namespace")]
        [InlineData("new")]
        [InlineData("null")]
        [InlineData("object")]
        [InlineData("operator")]
        [InlineData("out")]
        [InlineData("override")]
        [InlineData("params")]
        [InlineData("private")]
        [InlineData("protected")]
        [InlineData("public")]
        [InlineData("readonly")]
        [InlineData("ref")]
        [InlineData("return")]
        [InlineData("sbyte")]
        [InlineData("sealed")]
        [InlineData("short")]
        [InlineData("sizeof")]
        [InlineData("stackalloc")]
        [InlineData("this")]
        [InlineData("throw")]
        [InlineData("true")]
        [InlineData("try")]
        [InlineData("typeof")]
        [InlineData("uint")]
        [InlineData("ulong")]
        [InlineData("unchecked")]
        [InlineData("unsafe")]
        [InlineData("ushort")]
        [InlineData("using")]
        [InlineData("virtual")]
        [InlineData("void")]
        [InlineData("volatile")]
        [InlineData("while")]
        [Theory]
        public void CanMatchKeywords(string keyword)
        {
            var p = tokenizer.Keyword.ToPatternMatch();
            var s = new Scope(new Stream(new[] { keyword }));
            var m = p(s);
            Assert.True(m.Matched);
        }

        [Fact]
        public void IdentifierDoesntMatchKeywords()
        {
            var p = tokenizer.Identifier.ToPatternMatch();
            var s = new Scope(new Stream("while"));
            var m = p(s);
            Assert.False(m.Matched);
        }

        [Fact]
        public void IdentifierCanMatchKeywordsWithAt()
        {
            var p = tokenizer.Identifier.ToPatternMatch();
            var s = new Scope(new Stream("@while"));
            var m = p(s);
            Assert.True(m.Matched);
        }
    }
}
